# 前言
# js中的数据类型
JavaScript 中共有七种内置数据类型,包括基本类型和对象类型。
# 基本类型
基本类型(六种):
string
(字符串)boolean
(布尔值)number
(数字)symbol
(符号)null
(空值)undefined
(未定义)
string
、number
、boolean
和 null
undefined
这五种类型统称为原始类型(Primitive),表示不能再细分下去的基本类型。
symbol
是ES6中新增的数据类型,symbol
表示独一无二的值,通过 Symbol()
函数调用生成,由于生成的 symbol
值为原始类型,所以 Symbol()
函数不能使用 new
调用;
null
和 undefined
通常被认为是特殊值,这两种类型的值是唯一的,就是其本身。
# 对象类型
对象类型也叫引用类型,array
和 function
是对象的子类型。对象在逻辑上是属性的无序集合,是存放各种值的容器。对象值存储的是引用地址,所以和基本类型值不可变的特性不同,对象值是可变的。
# js是弱类型语言
JavaScript 是弱类型语言,而且JavaScript 声明变量的时候并没有预先确定的类型,变量的类型就是其值的类型,也就是说变量当前的类型由其值所决定,夸张点说上一秒种的String,下一秒可能就是个Number类型了,这个过程可能就进行了某些操作发生了强制类型转换。
虽然弱类型的这种不需要预先确定类型的特性给我们带来了便利,同时也会给我们带来困扰。为了能充分利用该特性就必须掌握类型转换的原理,
# 类型判断
有以下方式:
typeof
instanceof
Object.prototype.toString()
# typeof
只能判定除 null
以外的基本类型,对象只能区分 function
console.log(
typeof 'hello', // 'string'
typeof true, // 'boolean'
typeof 10, // 'number'
typeof Symbol(), // 'symbol'
typeof undefined, // 'undefined'
typeof null, // 'object' 无法判定是否为 null
typeof {}, // 'object'
typeof [], // 'object' 无法区分对象结构是否为数组
typeof(() => {}), // 'function'
)
2
3
4
5
6
7
8
9
10
11
12
# instanceof
通过 instanceof
操作符也可以对对象类型进行判定,其原理就是测试构造函数的 prototype
是否出现在被检测对象的原型链上。
[] instanceof Array // true
({}) instanceof Object // true
(()=>{}) instanceof Function // true
2
3
但 instanceof
也不是万能的。
const obj = {}
obj.__proto__ = Array.prototype
console.log(
[] instanceof Array, // true
[] instanceof Object, // true
({}) instanceof Object, // true
(()=>{}) instanceof Function, // true
(()=>{}) instanceof Object, // true
obj instanceof Object, // true
obj instanceof Array, // true
obj, // Array {}
)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
因为 Array
属于 Object
子类型,即:
Array.prototype.__proto__ === Object.prototype
Object
的构造函数在 数组实例 的原型链上。所以 instanceof
仍然无法准确地判断一个值到底属于数组还是普通对象。
# Object.prototype.toString()
这是判断数据类型最精确的方法。
console.log(
Object.prototype.toString.call({}), // '[object Object]'
Object.prototype.toString.call([]), // '[object Array]'
Object.prototype.toString.call(() => {}), // '[object Function]'
Object.prototype.toString.call('hello'), // '[object String]'
Object.prototype.toString.call(1), // '[object Number]'
Object.prototype.toString.call(true), // '[object Boolean]'
Object.prototype.toString.call(Symbol()), // '[object Symbol]'
Object.prototype.toString.call(null), // '[object Null]'
Object.prototype.toString.call(undefined), // '[object Undefined]'
Object.prototype.toString.call(new Date()), // '[object Date]'
Object.prototype.toString.call(Math), // '[object Math]'
Object.prototype.toString.call(new Set()), // '[object Set]'
Object.prototype.toString.call(new WeakSet()), // '[object WeakSet]'
Object.prototype.toString.call(new Map()), // '[object Map]'
Object.prototype.toString.call(new WeakMap()), // '[object WeakMap]'
)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
该方法本质就是依托
Object.prototype.toString()
方法得到对象内部属性[[Class]]
传入原始类型却能够判定出结果是因为对值进行了包装,比如new Number(1)
null
和undefined
能够输出结果是内部实现有做处理
# 类型转换
# 对象转换为原始值 ToPrimitive ( toString()/valueOf()
)
ToPrimitive对原始类型不发生转换处理,只针对引用类型(object)的,其目的是将引用类型(object)转换为非对象类型,也就是原始类型。
ToPrimitive 运算符接受一个值 obj
,和一个可选的 期望类型 type
作参数。ToPrimitive 运算符将值转换为非对象类型,如果对象有能力被转换为不止一种原语类型,可以使用可选的 期望类型 来暗示那个类型。
/**
* @obj 需要转换的对象
* @type 期望转换为的原始数据类型,可选
*/
ToPrimitive(obj,type)
2
3
4
5
6
type不同值的说明:
type
为'string'
:
先调用
obj
的toString
方法,如果为原始值,则return
,否则第2步调用
obj
的valueOf
方法,如果为原始值,则return
,否则第3步抛出
TypeError
异常
type
为'number'
:
调用
obj
的valueOf
方法,如果为原始值,则返回,否则下第2步调用
obj
的toString
方法,如果为原始值,则return
,否则第3步抛出
TypeError
异常
type
参数为空
该对象为
Date
,则type
被自动设置为string
否则,
type
被自动设置为Number
Date数据类型特殊说明:
对于Date数据类型,我们更多期望获得的是其转为时间后的字符串,而非毫秒值(时间戳),如果为 number
,则会取到对应的毫秒值,显然字符串使用更多。其他类型对象按照取值的类型操作即可。
ToPrimitive总结:
ToPrimitive转成何种原始类型,取决于type
,type
参数可选,若指定,则按照指定类型转换,若不指定,默认根据实用情况分两种情况,Date对象默认type
值为'string'
,其余对象为'number'
。
那么什么时候会指定 type
类型呢,那就要看下面两种转换方式了。
# toString()
Object.prototype.toString()
toString()
方法返回一个表示该对象的字符串。
每个对象都有一个 toString()
方法,当对象被表示为文本值时或者当以期望字符串的方式引用对象时,该方法被自动调用。
这里先记住,valueOf()
和 toString()
在特定的场合下会自行调用。
# valueOf()
Object.prototype.valueOf()
valueOf()
方法返回指定对象的原始值。
JavaScript 调用 valueOf()
方法用来把对象转换成原始类型的值(数值、字符串和布尔值)。但是很少需要手动调用此函数,通常都会被 JavaScript 自动调用。
# 不同内置对象默认的 valueOf
实现
- String => 返回字符串值
- Number => 返回数字值
- Date => 返回一个数字,即时间值,字符串中内容是依赖于具体实现的
- Boolean => 返回Boolean的this值
- Object => 返回this
var str = new String('123');
console.log(str.valueOf());//123
var num = new Number(123);
console.log(num.valueOf());//123
var date = new Date();
console.log(date.valueOf()); //1526990889729
var bool = new Boolean('123');
console.log(bool.valueOf());//true
var obj = new Object({valueOf:()=>{
return 1
}})
console.log(obj.valueOf());//1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 强制转换
# Number(param)
转换规则
param
为下列不同值时的转换规则:
null
转换为0
undefined
转换为NaN
true
转换为1
,false
转换为0
string
类型转换时遵循数字常量规则,转换失败返回NaN
object
类型要先转换为原始值,先调用 ToPrimitive 转换,type指定为number了
# String(param)
转换规则
param
为下列不同值时的转换规则:
null
转换为'null'
undefined
转换为'undefined'
true
转换为'true'
,false
转换为'false'
number
数字转换遵循通用规则,极大极小的数字使用指数形式object
对象这里要先转换为原始值,先调用 ToPrimitive 转换,type就指定为string了
String(null) // 'null'
String(undefined) // 'undefined'
String(true) // 'true'
String(1) // '1'
String(-1) // '-1'
String(0) // '0'
String(-0) // '0'
String(Math.pow(1000,10)) // '1e+30'
String(Infinity) // 'Infinity'
String(-Infinity) // '-Infinity'
String({}) // '[object Object]'
String([1,[2,3]]) // '1,2,3'
String(['koala',1]) //koala,1
2
3
4
5
6
7
8
9
10
11
12
13
# Boolean(param)
转换规则
param
为以下值时转换结果为 false
,否则为 true
:
undefined
null
0/+0/-0
NaN
''
(空字符串)
注意:所有对象(包括空对象)的转换结果都是true
,甚至连false
对应的布尔对象new Boolean(false)
也是true
Boolean(undefined) // false
Boolean(null) // false
Boolean(0) // false
Boolean(NaN) // false
Boolean('') // false
Boolean({}) // true
Boolean([]) // true
Boolean(new Boolean(false)) // true
2
3
4
5
6
7
8
9
# 隐式转换
# 会自动转换为 string
类型的情况
字符串 + 原始类型:后者自动转为字符串
console.log( '2' + 1, // '21' '2' + true, // "2true" '2' + false, // "2false" '2' + undefined, // "2undefined" '2' + null, // "2null" )
1
2
3
4
5
6
7字符串 + 对象:
// 字符串 + 几种对象 console.log( '2' + { a: 1 }, // '2[object Object]' '2' + [], // '2' '2' + function () {}, // '2function () {}' '2' + ['koala',1], // '2koala,1' ) // 字符串 + 重写了 toString() / valueOf() 方法的对象 const obj = { toString () { return 'a' }, valueOf () { return 9 } } // 在 + 号运算符中,对象的 ToPrimitive 转换 type 都默认为 'number',先调用 valueOf。 console.log( 1 + obj, // 10 '1' + obj, // '19' true + obj // 10 )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
注意:不管是不是对象,都有一个转换为原始值的过程,也就是ToPrimitive转换,只不过原始类型转换后不发生变化,对象类型才会发生具体转换。
# 会自动转换为 number
类型的情况
在加法运算符中,没有
string
类型的时候,自动转换为number
类型true + 0 // 1 true + true // 2 true + false //1
1
2
3除了加法运算符,其他运算符都自动转换为
number
类型console.log( '5' - '2', // 3 '5' * '2', // 10 true - 1, // 0 false - 1, // -1 '1' - 1, // 0 '5' * [], // 0 false / '5', // 0 'abc' - 1, // NaN null + 1, // 1 undefined + 1, // NaN ) //一元运算符也会转为 number 类型 console.log( +'abc', // NaN -'abc', // NaN +true, // 1 -false, // 0 )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20==
抽象相等比较时,也是自动转为 number 类型(==
两边都执行Number()
)// 如果存在对象,ToPrimitive() 的 type 参数默认值为 number 进行转换,再进行比较 const obj1 = { valueOf () { return '1' } } console.log( 1 == obj1 //true //相当于 Number(1) == Number(obj) ) console.log( [] == [], // false // 不发生类型转换 [] == ![], // true // ![] 为 false, 相当于 Number([]) == Number(false) ) // [] 作为对象,ToPrimitive 转换调用 valueOf() 得到 [], 再调用 toString() 得到 '' // Number('') == Number(false) // 0 == 0 // true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 会自动转换为 boolean
类型的情况
变量 a 在以下情况会被自动转换为 boolean
值,执行 Boolean()
操作。
!a
a && b
if (a) {}
while (a) {}
a ? 1 : 0
- ...
# NaN
NaN
是一个全局对象的属性,NaN
是一种特殊的 number
类型。
返回 NaN
的情况:
- 无穷大除以无穷大
- 给任意负数做开方运算
- 算数运算符两侧包含 非数值 或 无法转换为数值的值
- 字符串无法被解析成数值
// 以下全部返回 NaN
console.log(
Infinity / Infinity, // 无穷大除以无穷大
Math.sqrt(-1), // 给任意负数做开方运算
'a' - 1, // 算数运算符两侧包含 非数值 或 无法转换为数值的值
'a' * 1, // 同上
'a' / 1, // 同上
parseInt('a'), // 字符串无法被解析成数值
parseFloat('a'), // 同上
Number('a'), // 同上
+'abc', // 同上
-'abc', // 同上
)
2
3
4
5
6
7
8
9
10
11
12
13
14
# toString()
和 String()
的区别
# toString()
toString()
可以将数据都转为字符串,但是null
和undefined
不可以转换。//报错 TypeError: Cannot read property 'toString' of null console.log(null.toString()) //报错 TypeError: Cannot read property 'toString' of undefined console.log(undefined.toString())
1
2
3
4
5toString()
括号中可以写数字,代表进制let num = 10 console.log( num.toString(2), // 1010 二进制 num.toString(8), // 12 八进制 num.toString(), // 10 十进制 num.toString(16), // a 十六进制 )
1
2
3
4
5
6
7
# String()
String()
可以将 null
和 undefined
转换为字符串,但是没法转进制字符串
console.log(
String(null), // null
String(undefined), // undefined
)
2
3
4